# advanced_features_demo.py
"""
A comprehensive demo of advanced Python features including:
- Dataclasses
- Generics
- Async/Await
- Context Managers
- Decorators with arguments
- Type Aliases and NewType
- Basic data structures and classes
"""

import asyncio
import time
import logging
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import TypeVar, Generic, List, Dict, Optional, Callable, Any, NewType
from functools import wraps

# --- 1. Type Aliases and NewType ---
# 提高可读性
BookID = NewType('BookID', int)
UserID = NewType('UserID', int)
Email = NewType('Email', str)  # Conceptual type safety


# --- 2. Dataclasses ---
@dataclass
class Book:
    """使用 dataclass 简化数据类定义"""
    book_id: BookID
    title: str
    author: str
    isbn: str
    available: bool = True

    def __str__(self) -> str:
        status = "Available" if self.available else "Checked Out"
        return f"'{self.title}' by {self.author} ({status})"


@dataclass
class User:
    """另一个 dataclass 示例"""
    user_id: UserID
    name: str
    email: Email
    borrowed_books: List[BookID] = field(default_factory=list)  # Mutable default


# --- 3. Generics ---
# 定义一个泛型类型变量
T = TypeVar('T')


class Stack(Generic[T]):
    """一个泛型栈类，可以存储任何类型的元素"""

    def __init__(self) -> None:
        self._items: List[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> Optional[T]:
        if self.is_empty():
            return None
        return self._items.pop()

    def is_empty(self) -> bool:
        return len(self._items) == 0

    def peek(self) -> Optional[T]:
        if self.is_empty():
            return None
        return self._items[-1]

    def size(self) -> int:
        return len(self._items)


# --- 4. Decorators with arguments ---
def retry(max_attempts: int = 3, delay: float = 1.0):
    """带参数的装饰器：重试失败的函数"""

    def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
        @wraps(func)  # 保留原函数元信息
        async def wrapper(*args, **kwargs):
            attempts = 0
            while attempts < max_attempts:
                try:
                    # 假设被装饰的函数可能是异步的
                    if asyncio.iscoroutinefunction(func):
                        return await func(*args, **kwargs)
                    else:
                        return func(*args, **kwargs)
                except Exception as e:
                    attempts += 1
                    if attempts == max_attempts:
                        raise e
                    print(f"Attempt {attempts} failed: {e}. Retrying in {delay}s...")
                    await asyncio.sleep(delay)
            return None  # Should not reach here

        return wrapper

    return decorator


# --- 5. Context Managers ---
@contextmanager
def log_operation(operation_name: str):
    """一个简单的上下文管理器，用于记录操作开始和结束"""
    start_time = time.time()
    logging.info(f"Starting operation: {operation_name}")
    try:
        yield
    except Exception as e:
        logging.error(f"Operation '{operation_name}' failed with error: {e}")
        raise
    finally:
        end_time = time.time()
        logging.info(f"Finished operation: {operation_name} (Duration: {end_time - start_time:.2f}s)")


# --- 6. Basic Class with Special Methods ---
class LibraryError(Exception):
    """自定义异常"""
    pass


class Library:
    """一个简化版的图书馆类"""

    def __init__(self) -> None:
        self.books: Dict[BookID, Book] = {}
        self.users: Dict[UserID, User] = {}
        self._next_book_id: BookID = BookID(1)
        self._next_user_id: UserID = UserID(1)

    def add_book(self, title: str, author: str, isbn: str) -> BookID:
        """添加书籍"""
        book_id = self._next_book_id
        self.books[book_id] = Book(book_id, title, author, isbn)
        self._next_book_id = BookID(book_id + 1)
        return book_id

    def register_user(self, name: str, email: Email) -> UserID:
        """注册用户"""
        user_id = self._next_user_id
        self.users[user_id] = User(user_id, name, email)
        self._next_user_id = UserID(user_id + 1)
        return user_id

    # 在 advanced_features_demo.py 文件中找到 find_book 方法并替换为：

    def find_book(self, query: str) -> List[Book]:
        """根据标题或作者查找书籍 (不区分大小写)"""
        results = []
        query_lower = query.lower()
        for book in self.books.values():
            if query_lower in book.title.lower() or query_lower in book.author.lower():
                results.append(book)
        return results

    def check_out_book(self, user_id: UserID, book_id: BookID) -> None:
        """借书"""
        if user_id not in self.users:
            raise LibraryError(f"User ID {user_id} not found.")
        if book_id not in self.books:
            raise LibraryError(f"Book ID {book_id} not found.")
        book = self.books[book_id]
        if not book.available:
            raise LibraryError(f"Book '{book.title}' is not available.")

        book.available = False
        self.users[user_id].borrowed_books.append(book_id)

    def return_book(self, user_id: UserID, book_id: BookID) -> None:
        """还书"""
        if user_id not in self.users:
            raise LibraryError(f"User ID {user_id} not found.")
        if book_id not in self.books:
            raise LibraryError(f"Book ID {book_id} not found.")
        book = self.books[book_id]
        if book.available:
            raise LibraryError(f"Book '{book.title}' was not checked out.")
        if book_id not in self.users[user_id].borrowed_books:
            raise LibraryError(f"User {user_id} did not borrow book {book_id}.")

        book.available = True
        self.users[user_id].borrowed_books.remove(book_id)

    def __len__(self) -> int:
        """返回图书馆藏书数量"""
        return len(self.books)

    def __contains__(self, book_id: BookID) -> bool:
        """检查图书馆是否包含某本书"""
        return book_id in self.books


# --- 7. Async Functions ---
async def fetch_book_details_from_api(isbn: str) -> Dict[str, str]:
    """模拟一个异步API调用获取书籍详情"""
    # 模拟网络延迟
    await asyncio.sleep(0.1)
    # 模拟一个可能失败的请求
    if isbn == "999-9999999999":
        raise LibraryError("API Error: Book not found in external database.")
    return {
        "title": f"Title for ISBN {isbn}",
        "author": f"Author of {isbn}",
        "description": f"A detailed description for the book with ISBN {isbn}."
    }


@retry(max_attempts=2, delay=0.2)
async def get_book_info(library: Library, isbn: str) -> Optional[Dict[str, str]]:
    """使用重试装饰器获取书籍信息"""
    return await fetch_book_details_from_api(isbn)


# --- 8. Generator Function ---
def get_available_books(library: Library):
    """生成器：遍历所有可借阅的书籍"""
    for book in library.books.values():
        if book.available:
            yield book